#include "..\inc\Thread.h"

#include <mmsystem.h>
#include <process.h>
#include "SvrBase.h"
#include "ExceptionMacro.h"

namespace sbase
{


	//////////////////////////////////////////////////////////////////////
	// Construction/Destruction
	//////////////////////////////////////////////////////////////////////
	CThread::CThread(IThreadEvent& event)
		: m_event(event), m_hThread(NULL), m_hExitEvent(NULL), m_hWorkEvent(NULL)
	{
		m_id				= 0;
		m_dwWorkInterval	= 0;
		m_nStatus			= STATUS_INIT;
	}

	CThread::~CThread()
	{
		if (STATUS_INIT == m_nStatus)
			return;

		// resume if suspend
		if (STATUS_SUSPEND == m_nStatus)
			this->Resume();

		// close thread...
		if (STATUS_CLOSING != m_nStatus)
			this->Close();

		// waiting for close
		if (WAIT_TIMEOUT == ::WaitForSingleObject(m_hThread, TIME_WAITINGCLOSE))
		{
			::TerminateThread(m_hThread, 4444);
		}

		// release all resources
		if (m_hThread)
		{
			::CloseHandle(m_hThread);
			// m_hThread		= NULL;
		}

		if (m_hWorkEvent)
		{
			::CloseHandle(m_hWorkEvent);
			m_hWorkEvent	= NULL;
		}

		if (m_hExitEvent)
		{
			::CloseHandle(m_hExitEvent);
			m_hExitEvent	= NULL;
		}

		m_id = 0;
	}

	//////////////////////////////////////////////////////////////////////
	bool 
		CThread::Close()
	{
		if (::SetEvent(m_hExitEvent))
		{
			m_nStatus = STATUS_CLOSING;
			return true;
		}

		return false;
	}

	//////////////////////////////////////////////////////////////////////
	bool 
		CThread::Init(bool bSuspend/*=true*/, DWORD dwWorkInterval/*=INFINITE*/)
	{
		if (STATUS_INIT != m_nStatus)
			return false;

		// exit event
		m_hExitEvent = ::CreateEvent(	NULL,	// no security
			FALSE,	// auto reset
			FALSE,	// initial event reset
			NULL);	// no name
		if (!m_hExitEvent)
			return false;

		// work event
		m_hWorkEvent = ::CreateEvent(	NULL,	// no security
			FALSE,	// auto reset
			FALSE,	// initial event reset
			NULL);	// no name
		if (!m_hWorkEvent)
			return false;

		// thread
		DWORD dwCreationFlags = 0;
		if (bSuspend)
			dwCreationFlags	= CREATE_SUSPENDED;

		/*
		typedef unsigned int	(__stdcall *THREAD_FUNC)(void *);
		m_hThread	= (HANDLE)_beginthreadex (	NULL,			// default security
		0,				// default stack size
		(THREAD_FUNC)RunThread,		// pointer to thread routine
		dwCreationFlags,// start it right away if 0, else suspend
		(unsigned *)&m_id);
		*/

		typedef unsigned long	(__stdcall *THREAD_FUNC)(void *);
		m_hThread = ::CreateThread (NULL,						// default security
			0,							// default stack size
			(THREAD_FUNC)CThread::RunThread,			// pointer to thread routine
			(void *)this,				// argument for thread
			dwCreationFlags,			// start it right away if 0, else suspend
			(unsigned long*)&m_id);

		if (!m_hThread)
		{
			::CloseHandle(m_hExitEvent);
			m_hExitEvent = NULL;

			::CloseHandle(m_hWorkEvent);
			m_hWorkEvent = NULL;

			return false;
		}

		m_dwWorkInterval = dwWorkInterval;

		if (bSuspend)
			m_nStatus = STATUS_SUSPEND;
		else
			m_nStatus = STATUS_RUNNING;

		return true;
	}

	//////////////////////////////////////////////////////////////////////
	void 
		CThread::Resume(void)
	{
		if (STATUS_SUSPEND != m_nStatus)
			return;

		if (-1 != ::ResumeThread(m_hThread))
			m_nStatus = STATUS_RUNNING;
	}

	//////////////////////////////////////////////////////////////////////
	void 
		CThread::Suspend(void)
	{
		if (STATUS_RUNNING != m_nStatus)
			return;

		if (-1 != ::SuspendThread(m_hThread))
			m_nStatus = STATUS_SUSPEND;
	}


	//////////////////////////////////////////////////////////////////////
	// static
	//////////////////////////////////////////////////////////////////////
	CThread*	
		CThread::CreateNew(IThreadEvent& refEvent, bool bSuspend/* = SUSPEND*/, DWORD dwWorkInterval/* = INFINITE*/)
	{
		CThread* pThread = new CThread(refEvent);
		if (!pThread)
			return NULL;

		if (!pThread->Init(bSuspend, dwWorkInterval))
		{
			delete pThread;
			return NULL;
		}

		return pThread;
	}

	//////////////////////////////////////////////////////////////////////
	DWORD WINAPI 
		CThread::RunThread(LPVOID pThreadParameter)
	{
		CThread*	pThread	= (CThread*)pThreadParameter;
		if (!pThread)
			return 1;
		
		SEH_TRY
		// init
		if (-1 == pThread->m_event.OnThreadCreate())
			return 2;

		// work now
		HANDLE hEvent[2] = { pThread->m_hExitEvent, pThread->m_hWorkEvent };
		while (true)
		{
			DWORD dwTimeWait = pThread->m_dwWorkInterval;
			int rval = ::WaitForMultipleObjects(2, hEvent, false, dwTimeWait);
			if (WAIT_OBJECT_0 == rval)
			{
				// exit event is set
				break;
			}
			else if (WAIT_OBJECT_0+1 == rval)
			{
				// work event is set
				if (-1 == pThread->m_event.OnThreadEvent())
					break;
			}
			else if (WAIT_TIMEOUT == rval)
			{
				// work interval is time out
				if (-1 == pThread->m_event.OnThreadProcess())
					break;
				
			}
			else if (STATUS_CLOSING == pThread->m_nStatus)
			{
				break;
			}
			else
			{
				// here should log error msg
				break;
			}
		}

		pThread->m_nStatus = CThread::STATUS_CLOSED;
		return pThread->m_event.OnThreadDestroy();

		SEH_CATCH(pThread->m_szThreadName)
		return 0;
	}
}
